#ifndef		__ENGINE_CRYPTO_H_INCLUDED__
#define		__ENGINE_CRYPTO_H_INCLUDED__

#include	<cstdint>
#include	<string>

/**
 * Calculate a data's CRC32.
 *
 * \param mem Pointer to data to be calculated.
 * \param size Size of data in bytes.
 * \param pre_crc If this data is subset of a whole buffer, you should pass the result of CalcCRC before.
 * \return CRC32 result.
 **/
extern uint32_t CalcCRC(const char * mem, size_t size, uint32_t pre_crc = 0);

/**
 * Data hash using BKDRHash
 *
 * \param mem Pointer to data to be calculated.
 * \param size Size of data in bytes.
 * \return Hash result.
 **/
extern uint32_t	CalcHash(const char * mem, size_t size);

/**
 * MD5 tools.
 **/
class MD5 {
public:
	MD5() { Reset(); }
	MD5(const char * mem, size_t size) { Reset(); Update(mem, size); }
	MD5(const std::string & str) { Reset(); Update(str); }

	void	Update(const char * mem, size_t size);
	void	Update(const std::string & str);

	const uint8_t *	Digest();
	std::string		ToString();
	void			Copy(const uint8_t * digest);
	void			Reset();

private:
	void	__Transform();
	void	__Final();

private:
	bool		_finished;
	size_t		_size;
	uint8_t		_digest[16];
	uint8_t		_tail[64];
	uint32_t	_state[4];
};

/**
 * SHA-1 tools
 */
class SHA1 {
public:
	SHA1() { Reset(); }
	SHA1(const void * mem, size_t size) { Reset(); Update(mem, size); }
	SHA1(const std::string & str) { Reset(); Update(str); }

	void	Update(const void * mem, size_t size);
	void	Update(const std::string & str);

	const uint8_t *	Digest();
	std::string		ToString();
	void			Copy(const uint8_t * digest);
	void			Reset();

private:
	void	__Transform();
	void	__Final();

private:
	bool		_finished;
	size_t		_size;
	uint8_t		_digest[20];
	uint8_t		_tail[64];
	uint32_t	_state[5];
};

/**
 * HMAC-SHA1
 */
class HMAC_SHA1 {
public:
	HMAC_SHA1(const std::string & key);

	void Update(const void * mem, size_t size) { _sha1.Update(mem, size); }
	void Update(const std::string & str) { _sha1.Update(str); }

	const uint8_t *	Digest() { __Final(); return _sha1.Digest(); }
	std::string		ToString() { __Final(); return _sha1.ToString(); }
	void			Reset();

private:
	void	__Final();

private:
	bool	_finished;
	uint8_t	_o_key[64];
	uint8_t	_i_key[64];
	SHA1	_sha1;
};

#endif//!	__ENGINE_CRYPTO_H_INCLUDED__